﻿using System.Collections.Generic;
using Daemon.Service.Contract;
using Daemon.Model;
using Daemon.Data.Substructure.Framework;
using Microsoft.AspNetCore.Http;
using System.Linq;
using System;
using Microsoft.AspNetCore.Mvc;
using Daemon.Repository.Contract;
using Newtonsoft.Json;
using Daemon.Model.ViewModel;
using System.Threading.Tasks;
using System.Data.Common;
using Daemon.Data.Infrastructure.FrameWork.Query;
using MySqlConnector;
using Daemon.Middleware;
using Daemon.Common.Exceptions;
using Daemon.Model.Entities;
using Microsoft.EntityFrameworkCore;
namespace Daemon.Service
{
    public class ScoreImportService : IScoreImportService
    {
        private ImportTableResult CurrentTableResult { get; set; }
        private IDictionary<string, ImportTableResult> TableResults { get; } = new Dictionary<string, ImportTableResult>(StringComparer.OrdinalIgnoreCase);
        private readonly IScoreImportTemplateRepository _scoreImportTemplateRepository;

        private readonly IStudentRepository _studentRepository;

        private readonly OperationManager _operationManager;

        private readonly IStudentScoreRepository _studentScoreRepository;
        private readonly IHttpContextAccessor _httpContextAccessor;
        public int CurrentRowIndex { get; protected set; }

        public ScoreImportService(IScoreImportTemplateRepository scoreImportTemplateRepository,
        IStudentRepository studentRepository,
        OperationManager operationManager,
        IStudentScoreRepository studentScoreRepository,
        IHttpContextAccessor httpContextAccessor)
        {
            _scoreImportTemplateRepository = scoreImportTemplateRepository;
            _studentRepository = studentRepository;
            _operationManager = operationManager;
            _studentScoreRepository = studentScoreRepository;
            _httpContextAccessor = httpContextAccessor;
        }

        public ImportDataResult ImportScore(Dictionary<string, List<List<string>>> dic, int templateId, int gradeId, int classId, int stageId)
        {
            var result = new ImportDataResult();
            var template = _scoreImportTemplateRepository.FindById(templateId);
            _studentRepository.SetRelationships(new List<Relationship>() { new Relationship() { Name = "All" } });
            var students = _studentRepository.IncludeRelationships(_studentRepository.FindAll().Where(r => r.ClassId == classId && r.GradeId == gradeId)).ToList();
            _studentScoreRepository.SetRelationships(new List<Relationship>() { new Relationship() { Name = "All" } });
            var studentScore = _studentScoreRepository.IncludeRelationships(_studentScoreRepository.FindAll().Where(r => r.ClassId == classId && r.GradeId == gradeId && r.StageId == stageId)).ToList();
            Task.Run(() =>
            {
                Import(dic, template.TemplateJson, students, studentScore, stageId, result);
            });

            result.ChangedEvent += () =>
            {
                _operationManager.Set(result.Key, result);
            };

            return result;
        }

        protected ImportRowResult ImportRowCore(List<string> cellInfo, List<TemplateMapping> mapping, Student student, int stageId, string userId)
        {
            return InsertRow(cellInfo, mapping, student, stageId, userId);
        }

        protected string ValidateBeforeFillColumn(List<string> cellInfo)
        {
            return string.Empty;
        }

        protected string GenerateValidationMessage(string message)
        {
            return $@"行 {CurrentRowIndex} : {message}";
        }

        private void Import(Dictionary<string, List<List<string>>> dic, string templateJson, List<Student> students, List<StudentScore> studentScore, int stageId, ImportDataResult result)
        {
            var template = JsonConvert.DeserializeObject<List<TemplateMapping>>(templateJson);
            var studentSchoolMapping = template.FirstOrDefault(r => r.ColumnName == "School");
            var classNameMapping = template.FirstOrDefault(r => r.ColumnName == "Class");
            var studentNameMapping = template.FirstOrDefault(r => r.ColumnName == "Name");
            var startRowMapping = template.FirstOrDefault(r => r.ColumnName == "StartRow");
            var sheetNameMapping = template.FirstOrDefault(r => r.ColumnName == "SheetName");
            if (studentNameMapping == null || startRowMapping == null)
            {
                throw new BusinessException((int)BusinessCode.InCorrect, "模板配置错误！");
            }

            var userId = _httpContextAccessor.HttpContext.User.Claims.First(r => r.Type == "Id")?.Value;
            if (string.IsNullOrEmpty(userId))
            {
                throw new BusinessException((int)BusinessCode.InCorrect, "导入错误！请重新登录");
            }

            if (sheetNameMapping == null || sheetNameMapping.SheetName == "")
            {
                throw new BusinessException((int)BusinessCode.InCorrect, "模板配置错误！");
            }

            var cellInfos = dic[sheetNameMapping.SheetName];

            if (!startRowMapping.CellIndex.HasValue || !Int32.TryParse(startRowMapping.CellIndex.ToString(), out int startRow))
            {
                return;
            }

            if (cellInfos == null || cellInfos.Count == 0)
            {
                return;
            }

            if (result != null)
            {
                result.Changed();
            }

            if (!StartProcessTable(result, "student_score"))
            {
                return;
            }

            students = students.Where(r => r.AddUserId.ToString() == userId).ToList();
            result.CurrentTableResult.TotalSourceCount = cellInfos.Count;
            for (int i = startRow; i < cellInfos.Count; i++)
            {
                var cellInfo = cellInfos[i];
                CurrentRowIndex++;
                var studentNameCellIndex = studentNameMapping.CellIndex.Value;
                var studentName = cellInfo[studentNameCellIndex].ToString();
                if (studentSchoolMapping != null)
                {
                    var currentSchoolName = cellInfo[studentSchoolMapping.CellIndex.Value].ToString();
                    if (currentSchoolName != studentSchoolMapping.Value)
                    {
                        continue;
                    }
                }

                var importStudent = students.Find(r => r.Name == studentName);
                if (classNameMapping != null && classNameMapping.CellIndex.HasValue && importStudent != null)
                {
                    var currentClassName = cellInfo[classNameMapping.CellIndex.Value].ToString();
                    importStudent = importStudent.ClassName == currentClassName ? importStudent : null;
                }
                try
                {
                    if (importStudent == null)
                    {
                        if (result != null)
                        {
                            result.CurrentTableResult.AddMessage(GenerateValidationMessage($"没有找到学生{studentName}."));
                            if (result.CurrentTableResult != null)
                            {
                                result.CurrentTableResult.SkippedCount += 1;
                            }

                            FinishRow(result);
                        }
                        continue;
                    }

                    var message = ValidateBeforeFillColumn(cellInfo);
                    if (!string.IsNullOrEmpty(message))
                    {
                        if (result != null)
                        {
                            result.CurrentTableResult.AddMessage(GenerateValidationMessage(message));
                            if (result.CurrentTableResult != null)
                            {
                                result.CurrentTableResult.SkippedCount += 1;
                            }

                            FinishRow(result);
                        }
                        continue;
                    }

                    var duplicateScore = studentScore.Exists(r => r.Id == importStudent.Id && r.StageId == stageId);
                    if (duplicateScore)
                    {
                        result.CurrentTableResult.AddMessage(GenerateValidationMessage("Duplicate data exist in the database."));
                        SkipRow(result);
                    }

                    var importResult = ImportRowCore(cellInfo, template, importStudent, stageId, userId);
                    switch (importResult.Type)
                    {
                        case ImportRowResultType.Failed:
                            FailRow(result, cellInfo, template);
                            break;
                        case ImportRowResultType.Inserted:
                            InsertTableRow(result);
                            break;
                        case ImportRowResultType.Skipped:
                            SkipRow(result);
                            break;
                    }
                }
                catch (Exception ex)
                {
                    var message = $"{ex.Message}{(ex.InnerException != null ? ",内部错误:" + ex.InnerException.Message : $"发生了未知的错误！学生姓名为{studentName}.")}";
                    result.CurrentTableResult.AddError($"行 {CurrentRowIndex}:" + message);
                }
            }

            result.CurrentTableResult.Finish();
        }

        private bool StartProcessTable(ImportDataResult result, string tableName)
        {
            if (TableResults.ContainsKey(tableName))
            {
                return false;
            }

            var tableResult = new ImportTableResult(tableName, TableResults.Count);
            TableResults.Add(tableName, tableResult);
            CurrentTableResult = tableResult;
            if (result != null)
            {
                result.CurrentTableResult = tableResult;
            }

            return true;
        }

        private void FailRow(ImportDataResult result, List<string> cellInfo, List<TemplateMapping> mapping)
        {
            var errorMsg = "Failed to process item：";
            mapping.ForEach(item =>
            {
                errorMsg += $"{item.ColumnName}:{cellInfo[item.CellIndex.Value]},";
            });

            result?.CurrentTableResult.AddError(errorMsg.TrimEnd(','));
        }

        private void SkipRow(ImportDataResult result)
        {
            if (result != null)
            {
                if (result.CurrentTableResult != null)
                {
                    result.CurrentTableResult.SkippedCount++;
                }

                FinishRow(result);
            }
        }

        private ImportRowResult InsertRow(List<string> cellInfo, List<TemplateMapping> mapping, Student student, int stageId, string userId)
        {
            var paramNames = new List<string>();
            var parameters = new List<DbParameter>();
            if (mapping.Any())
            {
                mapping.ForEach(r =>
                {
                    if (r.ColumnName != "StartRow" && r.ColumnName != "Name" && r.ColumnName != "School" && r.ColumnName != "Class" && r.ColumnName != "SheetName" && r.CellIndex.HasValue)
                    {
                        paramNames.Add($"@{r.ColumnName}");
                        if (r.ColumnName.IndexOf("Score") > -1)
                        {
                            if (decimal.TryParse(cellInfo[r.CellIndex.Value], out decimal scoreValue))
                            {
                                parameters.Add(new MySqlParameter($"@{r.ColumnName}", scoreValue));
                            }
                            else
                            {
                                parameters.Add(new MySqlParameter($"@{r.ColumnName}", 0));
                            }
                        }
                        else
                        {
                            parameters.Add(new MySqlParameter($"@{r.ColumnName}", cellInfo[r.CellIndex.Value]));
                        }
                    }
                });

                paramNames.Add($"@StudentId");
                parameters.Add(new MySqlParameter($"@StudentId", student.Id));
                paramNames.Add($"@StageId");
                parameters.Add(new MySqlParameter($"@StageId", stageId));
                paramNames.Add($"@ClassId");
                parameters.Add(new MySqlParameter($"@ClassId", student.ClassId));
                paramNames.Add($"@GradeId");
                parameters.Add(new MySqlParameter($"@GradeId", student.GradeId));
                paramNames.Add($"@UserId");
                parameters.Add(new MySqlParameter($"@UserId", userId));
            }
            var insertSql = $"insert into student_score ({string.Join(",", mapping.Where(r => r.ColumnName != "StartRow" && r.ColumnName != "Class" && r.ColumnName != "School" && r.ColumnName != "Name" && r.ColumnName != "SheetName" && r.CellIndex.HasValue).Select(r => r.ColumnName))},StudentId,StageId,ClassId,GradeId,AddUserId) values ({string.Join(",", paramNames)})";
            insertSql += $";select  Id from student_score  order by Id desc LIMIT 1";
            var context = ServiceLocator.Resolve<ApiDBContent>();
            var con = context.Database.GetConnectionString();
            object keyValueOfInsertedRow = context.ExecuteScalar(insertSql, parameters);
            return ImportRowResult.CreateInsertedResult(keyValueOfInsertedRow);
        }

        private void InsertTableRow(ImportDataResult result, int count = 1)
        {
            if (result?.CurrentTableResult != null)
            {
                result.CurrentTableResult.InsertedCount += count;
            }

            FinishRow(result);
        }

        private void FinishRow(ImportDataResult result)
        {
            if (result != null)
            {
                if (result.CurrentTableResult != null)
                {
                    result.CurrentTableResult.ProcessedCount++;
                }

                result.ProcessedRowCount++;
            }
        }
    }
}
